package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.api.system.model.dto.CourseCategoryDTO;
import com.xuecheng.content.agent.SystemApiAgent;
import com.xuecheng.content.common.constant.CoursePubTemplateKey;
import com.xuecheng.content.common.domain.code.CommonErrorCode;
import com.xuecheng.content.common.domain.code.ContentErrorCode;
import com.xuecheng.content.common.domain.response.RestResponse;
import com.xuecheng.content.common.enums.common.CommonEnum;
import com.xuecheng.content.common.enums.content.CourseAuditEnum;
import com.xuecheng.content.common.enums.content.CourseModeEnum;
import com.xuecheng.content.common.exception.ExceptionCast;
import com.xuecheng.content.common.util.JsonUtil;
import com.xuecheng.content.convert.CoursePubConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.CourseMarket;
import com.xuecheng.content.entity.CoursePub;
import com.xuecheng.content.entity.CoursePubMsg;
import com.xuecheng.content.mapper.CoursePubMapper;
import com.xuecheng.content.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 课程发布 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class CoursePubServiceImpl extends ServiceImpl<CoursePubMapper, CoursePub> implements CoursePubService {

    @Autowired
    private CourseBaseService courseBaseService;

    @Autowired
    private CourseMarketService courseMarketService;

    @Autowired
    private TeachplanService teachplanService;

    @Autowired
    private SystemApiAgent systemApiAgent;

    @Autowired
    private CoursePubMsgService coursePubMsgService;

    //mq发送消息
    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Value("${course.publish.exchange}")
    private String exchange;
    @Value("${course.publish.queue}")
    private String queue;
    @Value("${course.publish.routingkey}")
    private String routingkey;


    /*
    课程预览
     */
    //主方法业务：
    //  1.构建CoursePub数据并保存
    //  2.根据CoursePub内容构架DataMap数据
    //  3.将DataMap进行返回

    @Override
    public Map<String, Object> previewCourse(Long courseId, Long companyId) {

        //1.构建CoursePub数据并保存
        CoursePub coursePub = generateCoursePub(courseId,companyId,false);


        // 2.根据CoursePub内容构架DataMap数据
        Map<String,Object> dataMap = generateDataMap(coursePub);

        // 3.将DataMap进行返回
        return dataMap;

    }

    private Map<String, Object> generateDataMap(CoursePub coursePub) {

        // 1.构建数据模型对象
        HashMap<String, Object> dataMap = new HashMap<>();

        // 2.构建coursePub
        dataMap.put(CoursePubTemplateKey.COURSEPUB, coursePub);

        // 3.构建courseMarket
        String marketJsonString = coursePub.getMarket();
        CourseMarket courseMarket = JsonUtil.jsonToObject(marketJsonString, CourseMarket.class);

        dataMap.put(CoursePubTemplateKey.COURSEMARKET,courseMarket );

        // 4.构建课程计划
        String teachplanJsonString = coursePub.getTeachplan();
        TeachplanDTO teachplanDTO = JsonUtil.jsonToObject(teachplanJsonString, TeachplanDTO.class);
        dataMap.put(CoursePubTemplateKey.TEACHPLANNODE,teachplanDTO );

        // 5.构建课程模式
        CourseModeEnum[] values = CourseModeEnum.values();
        dataMap.put(CoursePubTemplateKey.COURSETEACHMODEENUMS,values );

        return dataMap;
    }
    /*
    构建CoursePub数据并保存
  业务分析：
      1.判断关键数据
       courseId companyId
      2.判断业务数据
          课程基础信息
              判断是否存在
              判断是否是同一家机构
              判断是否删除
              判断审核状态：教学机构课程预览--未提交、审核未通过
          课程营销
              判断是否存在：根据courseid
          课程计划
              获得课程计划：根据courseId和companyId（树形结构）
          课程教师
              判断教师信息是否存在：一定要确保课程最少有一个教师信息
          课程分类数据并完善Coursepub数据
              调用system服务获得课程分类的名称
      3.保存课程发布数据
          CoursePub数据保存数据库中
              课程基础信息  课程营销  课程计划  课程教师
      4.将coursePub数据返回
     */
    private CoursePub generateCoursePub(Long courseId, Long companyId,boolean isPublish) {
        //1.判断关键数据
        //courseId companyId
        if (ObjectUtils.isEmpty(courseId)||ObjectUtils.isEmpty(companyId)){
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        // 2.判断业务数据
        //          课程基础信息
        //              判断是否存在
        //              判断是否是同一家机构
        //              判断是否删除
        //              判断审核状态：教学机构课程预览--未提交、审核未通过
        LambdaQueryWrapper<CourseBase> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CourseBase::getId,courseId);
        //是否在使用状态
        wrapper.eq(CourseBase::getStatus, CommonEnum.USING_FLAG.getCodeInt());
        wrapper.eq(CourseBase::getCompanyId,companyId);
        //判断是否存在
        CourseBase courseBase = courseBaseService.getOne(wrapper);
        if (ObjectUtils.isEmpty(courseBase)) {
            ExceptionCast.cast(ContentErrorCode.E_120416);
        }
        //判断审核状态：教学机构课程预览--未提交、审核未通过
        String auditStatus = courseBase.getAuditStatus();
        if (isPublish){
            if (!(CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus))) {
                ExceptionCast.cast(ContentErrorCode.E_120201);
            }
        }else{
            if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus)||
                    CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)||
                    CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
            ) {
                ExceptionCast.cast(ContentErrorCode.E_120015);
            }
        }

        //课程营销
        //              判断是否存在：根据courseid
        LambdaQueryWrapper<CourseMarket> marketQueryWrapper = new LambdaQueryWrapper<>();
        marketQueryWrapper.eq(CourseMarket::getCourseId, courseId);
        CourseMarket courseMarket = courseMarketService.getOne(marketQueryWrapper);
        if (ObjectUtils.isEmpty(courseMarket)){
            ExceptionCast.cast(CommonErrorCode.E_100104);
        }
        //课程计划
        //              获得课程计划：根据courseId和companyId（树形结构）
        TeachplanDTO teachplanTreeNodes = teachplanService.queryTreeNodesByCourseId(courseId, companyId);

        //     课程教师
        //         判断教师信息是否存在：一定要确保课程最少有一个教师信息
        // TODO: 2021/12/17 正在赶项目中，请稍后，谢谢
        //课程教师
        //      判断教师信息是否存在：一定要确保课程最少有一个教师信息



        //          课程分类数据并完善Coursepub数据
        //              调用system服务获得课程分类的名称
        String mt = courseBase.getMt();
        String st = courseBase.getSt();

        RestResponse<CourseCategoryDTO> mtResponse = systemApiAgent.getById(mt);

        if (!(mtResponse.isSuccessful())) {
            ExceptionCast.castWithCodeAndDesc(mtResponse.getCode(),mtResponse.getMsg());
        }

        RestResponse<CourseCategoryDTO> stResponse = systemApiAgent.getById(st);

        if (!(stResponse.isSuccessful())) {
            ExceptionCast.castWithCodeAndDesc(stResponse.getCode(),stResponse.getMsg());
        }
        // TODO: 2021/12/18 难懂呀，你个老掉
        CourseCategoryDTO mtEntity = mtResponse.getResult();
        CourseCategoryDTO stEntity = stResponse.getResult();

        // 构建coursePub数据并执行保存操作
        CoursePub coursePub = CoursePubConvert.INSTANCE.courseBase2coursePub(courseBase);

        // 课程营销数据
        String marketJsonString = JsonUtil.objectTojson(courseMarket);
        coursePub.setMarket(marketJsonString);
        coursePub.setPrice(courseMarket.getPrice());
        coursePub.setCharge(courseMarket.getCharge());

        // 课程计划数据
        String teachplanJsonString = JsonUtil.objectTojson(teachplanTreeNodes);
        coursePub.setTeachplan(teachplanJsonString);
        // 课程教师
        // todo 暂时没完成，正在努力中，、、、、、、、、、、、、、、
        // 课程分类数据
        coursePub.setMtName(mtEntity.getName());
        coursePub.setStName(stEntity.getName());


        // 3.保存课程发布数据
        //     CoursePub数据保存数据库中
        //         课程基础信息  课程营销  课程计划  课程教师
        // 一个courseBase数据对应一个coursePub数据
        LambdaQueryWrapper<CoursePub> pubQueryWrapper = new LambdaQueryWrapper<>();
        pubQueryWrapper.eq(CoursePub::getCourseId, courseId);
        pubQueryWrapper.eq(CoursePub::getCompanyId, companyId);

        CoursePub po = this.getOne(pubQueryWrapper);

        boolean result = false;
        // 判断课程发布数据是否存在
        if (ObjectUtils.isEmpty(po)) {
            //     如果不存在
            //             创建课程发布数据
            //     创建课程发布数据时，要将coursePub和courseBase数据进行关联
            coursePub.setCourseId(courseBase.getId());
            result = this.save(coursePub);
        } else {
            //     如果存在
            //             修改课程发布数据内容
            coursePub.setId(po.getId());
            result = this.updateById(coursePub);
        }

        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120018);
        }


        // 4.将coursePub数据返回
        return coursePub;


    }

    /*
   课程发布
          业务分析：
        1.判断关键数据
        2.判断业务数据
            课程基本信息
                判断是否存在
                判断是否是同一家机构
                判断是否删除
                判断审核状态
                    只能是审核通过的状态
        3.创建课程发布数据和发布消息数据
            课程发布数据
                课程基本信息 课程营销 课程计划 课程教师信息
            发布消息表
        4.发送消息给mq
            消息必须是有业务唯一标识：courseid
            发送消息：发布消息表
                转为json字符串
    */
    @Override
    public void publish(Long courseId, Long companyId) {
        //1.判断关键数据
        if (ObjectUtils.isEmpty(courseId)||ObjectUtils.isEmpty(companyId)){
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        //2.判断业务数据
        //      课程基本信息
        //          判断是否存在
        //          判断是否是同一家机构
        //          判断是否删除
        //          判断审核状态
        //              只能是审核通过的状态
//        LambdaQueryWrapper<CourseBase> courseBaseQueryWrapper = new LambdaQueryWrapper<>();
//        courseBaseQueryWrapper.eq(CourseBase::getId,courseId);
//        courseBaseQueryWrapper.eq(CourseBase::getCompanyId,companyId);
//        courseBaseQueryWrapper.eq(CourseBase::getStatus,CommonEnum.USING_FLAG.getCodeInt());
//        courseBaseQueryWrapper.eq(CourseBase::getAuditStatus, AuditEnum.AUDIT_PASTED_STATUS.getCode());
//        //判断是否为空
//        int count = this.count(courseBaseQueryWrapper);
//        if (count<1){
//            ExceptionCast.cast(CommonErrorCode.E_100104);
//        }
        // 2.判断业务数据
        //     课程基础信息
        //         判断是否存在
        //         判断是否是同一家机构
        //         判断是否删除
        //         判断审核状态
        //             必须是：审核通过
        // 3.保存数据的业务数据和消息数据
        //     业务数据：CoursePub数据进行保存
        CoursePub coursePub = this.generateCoursePub(courseId, companyId,true);

        //     消息数据：CoursePubMsg数据进行保存
        // 为了保证前端操作时，数据库中的消息表数据不会出现唯一约束的问题，需要在添加消息数据前查询是否有数据
        //         如果有不添加
        //         如果没有添加数据
        LambdaQueryWrapper<CoursePubMsg> pubMsgQueryWrapper = new LambdaQueryWrapper<>();
        pubMsgQueryWrapper.eq(CoursePubMsg::getCourseId,courseId);
        pubMsgQueryWrapper.eq(CoursePubMsg::getCompanyId,companyId);
        CoursePubMsg coursePubMsg = coursePubMsgService.getOne(pubMsgQueryWrapper);

        if (ObjectUtils.isEmpty(coursePubMsg)){
            coursePubMsg.setCourseId(courseId);
            coursePubMsg.setPubId(coursePub.getId());
            coursePubMsg.setCompanyId(companyId);
            coursePubMsg.setPubName(coursePub.getName());
            coursePubMsg.setPubStatus(coursePub.getStatus());
            boolean result = coursePubMsgService.save(coursePubMsg);

            if (!result){
                ExceptionCast.cast(ContentErrorCode.E_120416);
            }
        }
        // 4.发送消息给MQ
        //     指定：交换机名称、routingkey、发送消息CoursePubMsg数据、CorrelationData指定成功和失败方法

        /*
         * 参数：
         *   1.交互机名称
         *   2.routingkey名称
         *   3.发送的消息
         *   4.CorrelationData指定成功和失败方法
         * */
        String msgJsonString = JsonUtil.objectTojson(coursePubMsg);

        //构造方法中需要指定消息的id值（消息唯一标识）
        CorrelationData correlationData = new CorrelationData(courseId.toString());

        correlationData.getFuture().addCallback(
                //交换机响应成功
                confirm -> {
                    boolean ack = confirm.isAck();
                    //交换机成功接受消息， ack
                    if (ack){
                        //交换机成功接受消息后，要修改本地的数据状态
                        execuChangeLocalStatusData(correlationData.getId());
                    }
                },
                throwable -> {
                    log.error("发送课程发布的消息失败，失败的标识数据：{} , error: {}",correlationData.getId(),throwable.getMessage());
                }

        );
        rabbitTemplate.convertAndSend(exchange,routingkey,msgJsonString,correlationData);






    }
    /*
     * 修改本地数据的状态值：
     *       1.CourseBase
     *           审核状态：课程已发布
     *       2.CoursePubMsg
     *           发送成功的状态：0->1
     * */
    public void execuChangeLocalStatusData(String courseId){
        //1.修改课程基础信息中的审核状态
        LambdaUpdateWrapper<CourseBase> courseBaseUpdataeWrapper = new LambdaUpdateWrapper<>();
        courseBaseUpdataeWrapper.eq(CourseBase::getId,new Long(courseId));
        courseBaseUpdataeWrapper.set(CourseBase::getAuditStatus,CourseAuditEnum.AUDIT_PUBLISHED_STATUS);
        courseBaseUpdataeWrapper.set(CourseBase::getChangeDate, LocalDateTime.now());
        //判断修改课程表的信息是否成功
        boolean baseResult = courseBaseService.update(courseBaseUpdataeWrapper);

        if (!baseResult){
            log.error("课程发布修改课程审核状态失败：courseId: {}",courseId);
        }
        // 2.修改发布消息表中的状态
        LambdaUpdateWrapper<CoursePubMsg> pubMsgUpdateWrapper = new LambdaUpdateWrapper<>();
        pubMsgUpdateWrapper.eq(CoursePubMsg::getCourseId,new Long(courseId));
        pubMsgUpdateWrapper.set(CoursePubMsg::getPubStatus,CoursePubMsg.SENT);

        boolean update = coursePubMsgService.update(pubMsgUpdateWrapper);
        //判断是否修改成功
        if (!update){
            log.error("课程发布修改消息状态失败：courseId: {}",courseId);
        }


    }

}
